# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: xguo

import coin.exchange.base.kr_rest.private_client_base as privcb
import coin.exchange.fcoin.kr_rest.native_private_client as nprivc
from coin.exchange.fcoin.kr_rest.product import FcoinProduct
from coin.exchange.fcoin.kr_rest.currency import FcoinCurrency

import coin.proto.coin_order_enums_pb2 as coin_order
from coin.proto.coin_query_pb2 import (AccountBalance,
                                       CurrencyBalance,
                                       ProductFillElement,
                                       ProductFillBundle,
                                       ProductOrderElement,
                                       ProductOrderBundle)


def _get_order_side(side: str):
  if side == 'sell':
    side_proto = coin_order.SELL_ORDER
  elif side == 'buy':
    side_proto = coin_order.BUY_ORDER
  else:
    raise ValueError('Invalid order side %s' % side)
  return side_proto


class FcoinPrivateParser(object):
  @staticmethod
  def parse_balance(update_msg):
    assert update_msg['status'] == 0
    currency_balance_list = []
    for balance_info in update_msg['data']:
      currency = FcoinCurrency.FromStrNativeCurrencyNothrow(balance_info['currency'])
      if currency is None:
        continue
      currency_balance = CurrencyBalance(
          currency=currency.currency,
          currency_native=currency.native_currency,
          available=float(balance_info['available']),
          hold=float(balance_info['frozen']),
          total=float(balance_info['balance']),
      )
      currency_balance_list.append(currency_balance)

    return AccountBalance(exchange='Fcoin', market_type='Spot', each_balance=currency_balance_list)

  @staticmethod
  def parse_fill(update_msg, product) -> ProductFillBundle:
    assert update_msg['status'] == 0, update_msg
    currency_fill_list = []
    for order_info in update_msg['data']:
      assert order_info['symbol'] == product.native_symbol, order_info
      side = _get_order_side(order_info['side'])
      currency_fill = ProductFillElement(symbol=product.symbol,
                                         side=side,
                                         price=float(order_info['executed_value']),
                                         qty=float(order_info['filled_amount']),
                                         order_id=order_info['id'])
      currency_fill_list.append(currency_fill)

    return ProductFillBundle(exchange='Fcoin', symbol=product.symbol, each_fill=currency_fill_list)

  @staticmethod
  def parse_orders(update_msg, product):
    assert update_msg['status'] == 0, update_msg
    currency_order_list = []
    for order_info in update_msg['data']:
      exchange_order_id = order_info['id']
      side = _get_order_side(order_info['side'])
      currency_order = ProductOrderElement(
          symbol=product.symbol,
          side=side,
          price=float(order_info['price']),
          qty=float(order_info['amount']),
          order_id=exchange_order_id,
      )
      currency_order_list.append(currency_order)
    return ProductOrderBundle(exchange='Fcoin',
                              symbol=product.symbol,
                              each_order=currency_order_list)


class FcoinPrivateClient(privcb.PrivateClientBase):
  ProductType = FcoinProduct

  def __init__(self, key_file):
    privcb.PrivateClientBase.__init__(self)
    self.nprivc = nprivc.FcoinNativePrivateClient(key_file=key_file)

  def query_account_balance_impl(self):
    update = self.nprivc.query_GET_accounts_balance()
    update.msg = FcoinPrivateParser.parse_balance(update.msg)
    return update

  def query_fills_impl(self, product):
    update = self.nprivc.query_GET_orders(product.native_symbol, states='filled')
    update.msg = FcoinPrivateParser.parse_fill(update.msg, product)
    return update

  def query_list_orders_impl(self, product):
    update = self.nprivc.query_GET_orders(product.native_symbol, states='submitted')
    update.msg = FcoinPrivateParser.parse_orders(update.native_msg, product)
    return update

  def query_submit_impl(self, product, side, price, qty):
    raise NotImplementedError()

  def query_cancel_product_impl(self, product):
    raise NotImplementedError()
